package algorithm;

import entity.DeviceType;
import entity.FourTuple;
import entity.TwoTuple;
import problem.MLSIAP;
import problem.Problem;
import solution.SIAPSolution;
import solution.WaveForMLSIAP;

public class DEAlgForMLSIAP extends DEAlg<Integer, Double, SIAPSolution, FourTuple<Double, Double, Integer, Double>> {

	public DEAlgForMLSIAP(Problem<Integer, FourTuple<Double, Double, Integer, Double>, Double> problem, boolean isMax,
			String algName, int popSize, int NFE, double F, double CR) {
		super(problem, isMax, algName, popSize, NFE, F, CR);
		Mproblem = (MLSIAP) problem;
		getParameters();
	}

	MLSIAP Mproblem;
	int instanceId;
	int N1, N2, N;
	int M1, M2, M;
	int groupUpper, groupLower;
	int S;
	int L;// 长度

	private void getParameters() {
		instanceId = Mproblem.getInstanceId();
		N1 = Mproblem.getN1();
		N2 = Mproblem.getN2();
		N = Mproblem.getN();
		M1 = Mproblem.getM1();
		M2 = Mproblem.getM2();
		M = Mproblem.getM();
		groupUpper = Mproblem.getUpper();
		groupLower = Mproblem.getLower();
		S = Mproblem.getS();
		L = groupUpper - groupLower;
	}

	@Override
	public void initialize() {
		for (int i = 0; i < popSize; i++) {
			int j = 0;
			Integer[] content = new Integer[N + M];
			// 产生乘客分组的解
			for (; j < N; j++) {
				content[j] = random.nextInt(groupUpper - groupLower + 1) + groupLower;
			}
			// 产生设备人员分配的解
			assignInspectors(content);

			SIAPSolution solution = new SIAPSolution(content, groupUpper, groupLower, i, Mproblem.getDimension());
			population.add(solution);
		}

	}

	/**
	 * 根据每个设备th=Th情况下，检查人员检查乘客的总时间，降序排序，从前往后分配检查人员
	 * 
	 * @param content
	 */
	private void assignInspectors(Integer[] content) {
		DeviceType[] devices = Mproblem.calTotolTimeOfEveryDevice(content);
		// 防止空指针异常
		for (int i = N; i < N + M; i++) {
			content[i] = 0;
		}
		int temp = S;
		for (int i = 0; i < devices.length; i++) {
			// 没有可以额外分配的检查人员
			if (temp == 0) {
				break;
			} else if (temp >= devices[i].getNDh()) {
				content[N + devices[i].getH() - 1] = devices[i].getNDh();
				// 把分配的检查人员减掉
				temp -= devices[i].getNDh();
			} else {
				content[N + devices[i].getH() - 1] = temp;
				temp = 0;
			}
		}
	}

	@Override
	public void evolve() {
		// 对每个解都执行
		for (int i = 0; i < popSize; i++) {
			SIAPSolution solution = population.get(i);
			// 变异
			SIAPSolution mutatedVector = mutate(solution);
			// 交叉
			SIAPSolution trialVector = cross(solution, mutatedVector);
			// 分配检查人员
			assignInspectors(trialVector.getContent());
			// 选择
			SIAPSolution result = select(solution, trialVector);
			solution.setContent(result.getContent());
		}
	}

	@Override
	public SIAPSolution mutate(SIAPSolution target) {
		int targetIndex = target.getId();
		// 选出三个和目标索引不同的索引
		int[] randomIndex = selectKId(targetIndex, 3, popSize);
		Integer[] content = new Integer[target.getDimension()];
		SIAPSolution r1 = population.get(randomIndex[0]);
		SIAPSolution r2 = population.get(randomIndex[1]);
		SIAPSolution r3 = population.get(randomIndex[2]);
		Integer[] content1 = r1.getContent();
		Integer[] content2 = r2.getContent();
		Integer[] content3 = r3.getContent();
		double[] temp = new double[content1.length];
		for (int i = 0; i < content1.length; i++) {
			temp[i] = content1[i] + F * (content2[i] - content3[i]);
			temp[i] = temp[i] + L * F;
			content[i] = Math.round((float) temp[i]) % L;
		}
		return new SIAPSolution(content, groupUpper, groupLower, -1, content.length);
	}

	private int[] selectKId(int targetIndex, int k, int range) {
		// 选出k个索引
		int[] randomIndex = new int[k];
		for (int i = 0; i < k; i++) {
			randomIndex[i] = random.nextInt(range);
			boolean isUnique = false;
			while (!isUnique) {
				int j = 0;
				for (; j < i; j++) {
					if (randomIndex[i] == randomIndex[j] || randomIndex[i] == targetIndex) {
						// 重新生成一个索引
						randomIndex[i] = random.nextInt(range);
						break;
					}
				}

				// 没有重复
				if (j == i) {
					isUnique = true;
				}
			}
		}
		return randomIndex;
	}

	@Override
	public SIAPSolution cross(SIAPSolution originVector, SIAPSolution mutatedVector) {
		Integer[] content = new Integer[originVector.getDimension()];
		Integer[] Ocontent = originVector.getContent();
		Integer[] Mcontent = mutatedVector.getContent();
		int Irand = random.nextInt(N);
		for (int i = 0; i < content.length; i++) {
			double randI = random.nextDouble();
			if (randI <= CR || i == Irand) {
				content[i] = Mcontent[i];
			} else {
				content[i] = Ocontent[i];
			}
		}

		return new SIAPSolution(content, groupUpper, groupLower, -1, content.length);
	}

	@Override
	public SIAPSolution select(SIAPSolution originVector, SIAPSolution trialVector) {
		// 计算trialVector的值
		evaluateOneSolution(trialVector);
		if (originVector.getValue() < trialVector.getValue()) {
			return originVector;
		}
		return trialVector;
	}

	private void evaluateOneSolution(SIAPSolution solution) {
		// 计算适应度和约束值
		FourTuple<Double, Double, Integer, Double> result = Mproblem.calculate(solution);
		solution.setValue(result.getFirst());
		solution.setValueOfPFC(result.getSecond());
		solution.setTotalS(result.getThrid());
		solution.setMaxTime(result.getFourth());
		// 增加函数评估次数
		current_NFE++;
		// 统计
		if (current_NFE % 1000 == 0) {
			TwoTuple<Integer, SIAPSolution> record = new TwoTuple<Integer, SIAPSolution>(current_NFE, best);
			records.add(record);
		}
	}

	@Override
	protected void evaluate() {
		SIAPSolution[] solutions = new SIAPSolution[popSize];
		// 对种群中的每个解执行评估
		for (int i = 0; i < popSize; i++) {
			SIAPSolution solution = population.get(i);
			// 计算适应度和约束值
			evaluateOneSolution(solution);
			// 使用插入排序，将种群由小到大排序
			solutions[i] = solution;
			for (int j = i; j >= 1; j--) {
				if (solutions[j].getValue() < solutions[j - 1].getValue()) {
					SIAPSolution temp = solutions[j];
					solutions[j] = solutions[j - 1];
					solutions[j - 1] = temp;
				}
			}
		}
		// 得到最优解,最差解
		best = solutions[0];
		worst = solutions[solutions.length - 1];

	}

	@Override
	public void updateParameters() {
		// TODO Auto-generated method stub

	}

}
